using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System;

[System.Serializable]
public class CharacterAnimatedState: MonoBehaviour
{
	public AnimationClip animationClip = null; 
	private string animationClipName;
	public float animationSpeed = 1;
	public float animationBlend = 0.1f;  
	public CharacterAnimatedState nextState;
    public bool speedLinkTimeScale = false;

	// character related
	[System.NonSerialized] public Character character = null;
	[System.NonSerialized] public float duration = 0;
	[System.NonSerialized] public int frames = 0;

	[System.NonSerialized] public List<CharacterKeyFrame> keyFrames;

	[System.NonSerialized] public List<CharacterKeyFrame> curKeyFrames;

	//propretyies
	public string stateName { get { return gameObject.name; } }
	//[System.NonSerialized] 
	public int curFrame = 0;
	private int currentKeyFrameIndex = 0;  // to search next key frame faster

    public int loop = 0;
    private int curLoop = 0;
	// Init with Character
	public void Init(Character value)
	{
		if(value == null)
		{
			Debug.LogError(this.GetType().ToString() + " init without null character");
			return;
		}

		if(character != null)
		{
			Debug.LogError(this.GetType().ToString() + " init while character already exists");
		}

		name = gameObject.name;
		character = value;  
		if(character.animation.GetClip(animationClip.name) != null)
		{
			if(character.animation[animationClip.name] != animationClip)
				Debug.LogError(this.GetType() + animationClip.name + " two animationClips with the same name");
		}
		else
		{
			character.animation.AddClip(animationClip, animationClip.name);
		}

		keyFrames = new List<CharacterKeyFrame>(GetComponentsInChildren<CharacterKeyFrame>());
		keyFrames.Sort(CharacterKeyFrame.Compare);
        for(int i=0;i<keyFrames.Count;i++)
        {
            keyFrames[i].Init(this);
        }
		curKeyFrames = new List<CharacterKeyFrame>();
		InitFramesInfo();
 
	}
 
	public void InitFramesInfo()
	{
		if(animationClip != null)
		{
			duration = animationClip.length / animationSpeed;

            //kind of hack for unity animation sample framerate
			frames = 2*(int)(animationClip.length * animationClip.frameRate); 
		}
	}
	
	
	public virtual void Enter()
	{
		curFrame = 0;
		currentKeyFrameIndex = 0;
		curKeyFrames.Clear();
		duration = animationClip.length / animationSpeed;
        if(character.lockedAttackStatesNames.Contains(this.stateName))
        {
            character.FaceToTarget();
        }
        else
        {
            character.SetNullTarget();
        }
        PlayAnimation();
        if(speedLinkTimeScale)
        {
            Time.timeScale = animationSpeed;
        }
	}
	
	public virtual void Exit()
	{
        for(int i=0;i<curKeyFrames.Count;i++)
        {
            curKeyFrames[i].ExitFrame();
        }
		curKeyFrames.Clear();
		curFrame = 0;
        Time.timeScale = 1.0f;
	}

    public bool ReachLoopEnd()
    {
        return curLoop == loop;
    }

    public void ResetLoop()
    {
        curLoop = 0;
    }
	
	public virtual void EndState()
	{
        if(loop > 0)
        {
            if(curLoop < loop)
            {
                curLoop++;
                character.ToState(stateName);
                return;
            }
        }
		if(nextState != null)
		{
            ResetLoop();
			character.ToState(nextState.stateName);
		}
		else//for die state
		{
			//character.MonitersOnExit(curState.stateName);
            ResetLoop();
			Exit();
		}
	}

    public bool IsLastLoop()
    {
        return curLoop == loop;
    }
	
	public virtual void UpdateState()
	{
		//update action per state
		if(character.onStateUpdate.listened)
		{
			Character.StateUpdate message = new Character.StateUpdate();
			character.onStateUpdate.SendEventMessage(message);
		}

		float deltaTime = 1 * animationSpeed;
		float beginFrame = character.curFrameTime * animationSpeed / Time.fixedDeltaTime;
		float endFrame = beginFrame + animationSpeed;

		for(int i = Mathf.CeilToInt(beginFrame); i < endFrame && i < frames; i++)
		{
			UpdateFrame(i);
		}
	}

	protected virtual void UpdateFrame(int i)
	{
		curFrame = i;
		for(CharacterKeyFrame frame = NextKeyFrame(); 
		    frame != null && curFrame >= frame.start && (curFrame < frame.end || frame.end < 0); 
		    frame = NextKeyFrame())
		{
			if(curKeyFrames.Contains(frame) == false)
			{ 	// on enter
				curKeyFrames.Add(frame);
				frame.EnterFrame();
			}
		}
		PreviousKeyFrame();

		CharacterKeyFrame[] copyCurrent = curKeyFrames.ToArray();
		foreach(CharacterKeyFrame frame in copyCurrent)
		{
			if(curFrame >= frame.start && (curFrame < frame.end || frame.end < 0))
			{	
				if(character.onFrameUpdate.listened)
				{
					Character.FrameUpdate message = new Character.FrameUpdate();
					message.character = character;
					character.onFrameUpdate.SendEventMessage(message);
				}
			}
			else 
			{   // on exit
				curKeyFrames.Remove(frame);
				frame.ExitFrame();
			}
		}
	}
	
	private CharacterKeyFrame NextKeyFrame()
	{
		if(currentKeyFrameIndex < keyFrames.Count)
		{
			return keyFrames[currentKeyFrameIndex++];
		}
		else
			return null;
	}

	private CharacterKeyFrame PreviousKeyFrame()
	{
		if(currentKeyFrameIndex > 0)
		{
			return keyFrames[--currentKeyFrameIndex];
		}
		else
			return null;
	}

	#region Common_State_Features

	public void PlayAnimation()
	{ 
		string animationName = animationClip.name;

		character.animation[animationName].time = 0;
		character.animation[animationName].speed = animationSpeed;

		character.animation.CrossFade(animationName, animationBlend);
	}

	#endregion 
}

